/**
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

package pittypat.primitives;

import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.util.UUID;

import pittypat.Json;

import java.util.Base64;

/**
 * 实现了 IJson 的全局唯一标识符。
 */
public final class Uuid implements IPrimitive {
	private static final UUID MIN = UUID.fromString("00000000-0000-0000-0000-000000000000");
	private static final UUID MAX = UUID.fromString("ffffffff-ffff-ffff-ffff-ffffffffffff");

	/**
	 * 将指定的字符串转换为 UUID。
	 * 
	 * @param s
	 *            要转换的字符串。
	 * @return 从字符串转换到的 UUID，如果转换失败则返回 null。
	 */
	public static UUID parse(java.lang.String s) {
		if (s == null || s.length() == 0) {
			return null;
		}

		s = s.trim();
		if (s.length() == 0) {
			return null;
		}

		if (s.equalsIgnoreCase("min") || s.equalsIgnoreCase("minvalue") || s.equalsIgnoreCase("min_value")
				|| s.equalsIgnoreCase("zero") || s.equalsIgnoreCase("default") || s.equalsIgnoreCase("0")
				|| s.equalsIgnoreCase("empty")) {
			return MIN;
		}

		if (s.equalsIgnoreCase("max") || s.equalsIgnoreCase("maxvalue") || s.equalsIgnoreCase("max_value")
				|| s.equalsIgnoreCase("f")) {
			return MAX;
		}

		int len = s.length();
		if (len == 22 || len == 24) {
			return fromBase64(s);
		}

		try {
			if (len == 36) {
				// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
				return java.util.UUID.fromString(s);
			}

			if (len == 32) {
				// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
				return java.util.UUID
						.fromString(s.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
			}

			if (len == 38) {
				// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} or
				// (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
				return java.util.UUID.fromString(s.substring(1, 37));
			}
		} catch (Exception e) {
		}

		return null;
	}

	private java.util.UUID value;

	/**
	 * 使用指定的值初始化 Uuid 类的新实例。
	 * 
	 * @param value
	 *            值。
	 */
	public Uuid(java.util.UUID value) {
		this.value = value;
	}

	/**
	 * 获取基础类型的值。
	 * 
	 * @return 基础类型的值。
	 */
	public java.util.UUID getValue() {
		return this.value;
	}

	/**
	 * 将 Uuid 转换为对 URL 友好的 base64 字符串（'+' 和 '/' 分别替换为 '-' 和 '_'）。
	 */
	@Override
	public java.lang.String toString() {
		return toBase64(this.value);
	}

	/**
	 * 将当前对象作为 JSON 字符串写入指定的文本流。
	 * 
	 * @param writer
	 *            目标文本流。
	 * @throws IOException
	 * @see pittypat.IJson#write(java.io.Writer)
	 */
	@Override
	public void write(Writer writer) throws IOException {
		Json.write(writer, value);
	}

	/**
	 * 将指定的 base64 字符串转换为 UUID。
	 * 
	 * @param base64
	 *            要转换的字符串。
	 * @return 从字符串转换到的 UUID，如果转换失败则返回 null。
	 */
	public static UUID fromBase64(java.lang.String base64) {
		if (base64 == null) {
			return null;
		}

		java.lang.String s = base64.length() == 24 ? base64 : (base64.length() == 22 ? (base64 + "==") : null);
		if (s == null) {
			return null;
		}

		try {
			return fromBinary(Base64.getDecoder().decode(s.replace('_', '/').replace('-', '+')));
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 将指定的 UUID 转换为对 URL 友好的 base64 字符串（'+' 和 '/' 分别替换为 '-' 和 '_'）。
	 * 
	 * @param id
	 *            要转换的 UUID。
	 * @return 转换后的字符串，如果 id 为 null，则返回 null。
	 */
	public static java.lang.String toBase64(UUID id) {
		if (id == null) {
			return null;
		}

		java.lang.String s = Base64.getEncoder().encodeToString(toBinary(id));
		s = s.replace('+', '-').replace('/', '_').substring(0, 22);
		return s;
	}

	/**
	 * 将指定的字节数组转换为 UUID。
	 * 
	 * @param bytes
	 *            要转换的字节数组。
	 * @return 从字节数组转换到的 UUID，如果 bytes 为 null 或少于 16 个字节，则返回 null。
	 */
	public static UUID fromBinary(byte[] bytes) {
		if (bytes == null || bytes.length < 16) {
			return null;
		}

		ByteBuffer bb = ByteBuffer.wrap(bytes);
		long firstLong = bb.getLong();
		long secondLong = bb.getLong();
		return new UUID(firstLong, secondLong);
	}
	
	/**
	 * 将指定的 UUID 转换为字节数组。
	 * 
	 * @param id
	 *            要转换的 UUID。
	 * @return 转换后的字节数组，如果 id 为 null，则返回 null。
	 */
	public static byte[] toBinary(UUID id) {
		if (id == null) {
			return null;
		}

		ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
		bb.putLong(id.getMostSignificantBits());
		bb.putLong(id.getLeastSignificantBits());
		return bb.array();
	}
}
